package httpserver

import (
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
)

//转发http的handler
type HttpForwardHandler struct {
	forwardAddr  string
	forwardRules map[string]string
	client       http.Client
}

func NewHttpForwardHandler(forward string, timeout time.Duration) *HttpForwardHandler {
	if !strings.HasPrefix(forward, "http") {
		forward = "http://" + forward
	}
	return &HttpForwardHandler{
		forwardAddr:  forward,
		forwardRules: map[string]string{},
		client:       http.Client{Timeout: timeout,},
	}
}

func (h *HttpForwardHandler) AddForwardRule(rule map[string]string) {
	rules := h.forwardRules
	for path, forwardAddr := range rule {
		rules[path] = forwardAddr
	}

	h.forwardRules = rules
}

func (h *HttpForwardHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	forwardReq, err := h.copyReq(r)
	if err != nil {
		panic(err)
	}

	httpResp, err := h.client.Do(forwardReq)
	if err != nil {
		panic(err)
	}
	defer httpResp.Body.Close()
	repBody, err := ioutil.ReadAll(httpResp.Body)
	if err != nil {
		panic(err)
	}

	w.WriteHeader(httpResp.StatusCode)
	w.Write(repBody)
}
func (h *HttpForwardHandler) getForwardAddr(request *http.Request) string {
	uri := request.RequestURI
	forwardAddr := h.forwardAddr
	for path, addr := range h.forwardRules {
		if path == uri {
			forwardAddr = addr
		}

		if pos := strings.Index(path, "*"); pos >= 0 {
			prefix := path[:pos]
			suffix := path[pos+1:]
			if strings.HasPrefix(uri, prefix) && strings.HasSuffix(uri, suffix) {
				forwardAddr = addr
			}
		}
	}

	if !strings.HasPrefix(forwardAddr, "http") {
		forwardAddr = "http://" + forwardAddr
	}

	return forwardAddr
}
func (h *HttpForwardHandler) copyReq(request *http.Request) (*http.Request, error) {
	//
	forwardAddr := h.getForwardAddr(request)

	//
	forwardUrl, err := url.Parse(forwardAddr)
	if err != nil {
		return nil, err
	}

	request.ParseForm()
	forwardUrl.RawQuery = request.Form.Encode()

	forwardMethod := request.Method

	forwardReq, err := http.NewRequest(forwardMethod, forwardUrl.String(), request.Body)

	if err != nil {
		return nil, err
	}

	for headerKey := range request.Header {
		forwardReq.Header.Add(headerKey, request.Header.Get(headerKey))
	}

	forwardReq.Header.Add("ForwardSource", request.RequestURI)
	return forwardReq, nil
}
